/**
 * 621. 任务调度器
 * https://leetcode-cn.com/problems/task-scheduler/
 */

/**
 * @param {any[]} arr
 * @param {number} a
 * @param {number} b
 */
function swap(arr, a, b) {
  const temp = arr[a];
  arr[a] = arr[b];
  arr[b] = temp;
}

/**
 * @param {number[]} codes
 * @param {number} length
 * @param {number[]} lefts
 * @param {number[]} lasts
 * @param {number} freeze
 * @param {number} time
 */
function resolveCodes(codes, length, lefts, lasts, freeze, time) {
  let index = 0;
  let indexCode = 0;
  let indexCount = 0;
  let isIndexFrozen = true;
  let left = 0;
  let leftCode = 0;
  let leftCount = 0;
  let isLeftFrozen = true;
  let right = 0;
  let rightCode = 0;
  let rightCount = 0;
  let isRightFrozen = true;
  for (index = Math.floor(length / 2) - 1; index >= 0; index -= 1) {
    indexCode = codes[index];
    indexCount = lefts[indexCode];
    isIndexFrozen = time - lasts[indexCode] <= freeze;
    left = index * 2 + 1;
    leftCode = codes[left];
    leftCount = lefts[leftCode];
    isLeftFrozen = time - lasts[leftCode] <= freeze;
    right = left + 1;
    rightCode = codes[right];
    rightCount = right >= length ? 0 : lefts[rightCode];
    isRightFrozen = right >= length ? true : (time - lasts[rightCode] <= freeze);
    if (isIndexFrozen) {
      if (isLeftFrozen && !isRightFrozen) {
        swap(codes, index, right);
      } else if (!isLeftFrozen && isRightFrozen) {
        swap(codes, index, left);
      } else if (!isLeftFrozen && !isRightFrozen) {
        if (leftCount >= rightCount) {
          swap(codes, index, left);
        } else {
          swap(codes, index, right);
        }
      }
    } else {
      // eslint-disable-next-line no-lonely-if
      if (isLeftFrozen && !isRightFrozen) {
        if (rightCount > indexCount) {
          swap(codes, index, right);
        }
      } else if (!isLeftFrozen && isRightFrozen) {
        if (leftCount > indexCount) {
          swap(codes, index, left);
        }
      } else if (!isLeftFrozen && !isRightFrozen) {
        if (indexCount < leftCount || indexCount < rightCount) {
          if (leftCount >= rightCount) {
            swap(codes, index, left);
          } else {
            swap(codes, index, right);
          }
        }
      }
    }
  }
}

/**
 * @param {string[]} tasks
 * @param {number} n
 * @return {number}
 */
function leastInterval(tasks, n) {
  const lefts = new Array(128);
  const lasts = new Array(128);
  let codes = new Set();
  for (let i = 0; i < tasks.length; i += 1) {
    const code = tasks[i].charCodeAt(0);
    codes.add(code);
    lefts[code] = (lefts[code] || 0) + 1;
    lasts[code] = -Infinity;
  }
  codes = Array.from(codes);
  let leftCodeCount = codes.length;
  let currentTime = 0;
  let top = 0;
  while (leftCodeCount) {
    resolveCodes(codes, leftCodeCount, lefts, lasts, n, currentTime);
    [top] = codes;
    if (currentTime - lasts[top] > n) {
      // console.log(`"${String.fromCharCode(top)}"`);
      lasts[top] = currentTime;
      lefts[top] -= 1;
      if (lefts[top] <= 0) {
        leftCodeCount -= 1;
        swap(codes, 0, leftCodeCount);
      }
    } else {
      // console.log('" "');
    }
    currentTime += 1;
  }
  return currentTime;
}

console.log(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 2) === 8);
console.log(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 0) === 6);
console.log(leastInterval(['A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'D', 'E', 'F', 'G'], 2) === 16);
